package com.ml4ai.backend.utils;

import lombok.SneakyThrows;
import lombok.extern.apachecommons.CommonsLog;

import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by leecheng on 2017/11/13.
 */
@CommonsLog
public class Bean2Map {

    private List<PropertyMapper> mappers = new ArrayList<>();
    private List<String> ignores = new LinkedList<>();


    public Bean2Map(PropertyMapper... mappers) {
        this.mappers.addAll(Arrays.asList(mappers));
    }

    public Bean2Map addIgnores(String... props) {
        ignores.addAll(Arrays.asList(props));
        return this;
    }

    public Bean2Map addPropMapper(PropertyMapper... mapper) {
        this.mappers.addAll(Arrays.asList(mapper));
        return this;
    }

    @SneakyThrows
    public Map<String, Object> map(Object bean) {
        if (bean == null) return null;
        Map<String, PropertyMapper> mapperObjs = new HashMap<>();
        mappers.forEach(mapper -> mapperObjs.put(mapper.getSource(), mapper));
        Map<String, Object> result = new LinkedHashMap<>();
        Map<String, Method> excludeGetters = new LinkedHashMap<>();
        Class clz = bean.getClass();
        for (Method getter : clz.getMethods()) {
            if (getter.getName().startsWith("get") && getter.getParameterCount() == 0) {
                String getterName = getter.getName();
                String propName = getterName.substring(3);
                if (propName.substring(0, 1).equals(propName.substring(0, 1).toUpperCase())) {
                    propName = propName.substring(0, 1).toLowerCase() + propName.substring(1);
                }
                if (ignores.contains(propName)) continue;
                if (mapperObjs.keySet().contains(propName)) {
                    excludeGetters.put(propName, getter);
                    continue;
                } else {
                    Object val = getter.invoke(bean);
                    if (val == null) {
                        continue;
                    } else {
                        result.put(propName, val);
                    }
                }
            }
        }
        for (Map.Entry<String, Method> getterEnt : excludeGetters.entrySet()) {
            PropertyMapper mapper = mapperObjs.get(getterEnt.getKey());
            Method getter = getterEnt.getValue();
            Object val = getter.invoke(bean);
            if (val != null)
                result.put(mapper.getTarget(), mapper.map(getter.invoke(bean)));
        }
        return result;
    }
}
